﻿using System;
using System.Collections.Generic;
using System.Linq;
using MashGen.Engine.Expressions;

namespace MashGen.Engine.Matching
{
    public sealed class Matcher : IExpressionVisitor<object, Builder>
    {
        static Matcher() { }

        private static Matcher instance = new Matcher();

        private Matcher() { }

        public static List<Match> Match(AbstractExpression pattern, AbstractExpression expression)
        {
            var unifierBuilder = new Builder(pattern);
            expression.Vist(instance, unifierBuilder);
            return unifierBuilder.SuccessfullMatches;
        }

        object IExpressionVisitor<object, Builder>.VisitNumber(Number number, Builder builder)
        {
            builder.AddNewAttempt(number);
            builder.DoForEach(a => a.MatchNumber(number));
            builder.DoForEach(a => a.PopPosition());
            return null;
        }

        object IExpressionVisitor<object, Builder>.VisitVariable(Variable variable, Builder builder)
        {
            builder.AddNewAttempt(variable);
            builder.DoForEach(a => a.MatchVariable(variable));
            builder.DoForEach(a => a.PopPosition());
            return null;
        }

        object IExpressionVisitor<object, Builder>.VisitNegative(Negative negative, Builder builder)
        {
            builder.AddNewAttempt(negative);
            builder.DoForEach(a => a.MatchStructure(negative));

            builder.DoForEach(a => a.PushPosition<Negative>(n => n.Expression));
            negative.Expression.Vist(this, builder);

            builder.DoForEach(a => a.PopPosition());
            return null;
        }

        object IExpressionVisitor<object, Builder>.VisitSum(Sum sum, Builder builder)
        {
            builder.AddNewAttempt(sum);
            builder.DoForEach(a => a.MatchStructure(sum));

            //builder.DoForEach(a => a.PushPosition<Negative>(n => n.Expression));
            //negative.Expression.Vist(this, builder);

            builder.DoForEach(a => a.PopPosition());
            return null;
        }

        object IExpressionVisitor<object, Builder>.VisitProduct(Product product, Builder builder)
        {
            throw new NotImplementedException();
        }

        object IExpressionVisitor<object, Builder>.VisitQuotient(Quotient quotient, Builder builder)
        {
            builder.AddNewAttempt(quotient);
            builder.DoForEach(a => a.MatchStructure(quotient));

            builder.DoForEach(a => a.PushPosition<Quotient>(q => q.Numerator));
            quotient.Numerator.Vist(this, builder);

            builder.DoForEach(a => a.PushPosition<Quotient>(q => q.Denominator));
            quotient.Denominator.Vist(this, builder);

            builder.DoForEach(a => a.PopPosition());
            return null;
        }

        object IExpressionVisitor<object, Builder>.VistPower(Power power, Builder builder)
        {
            builder.AddNewAttempt(power);
            builder.DoForEach(a => a.MatchStructure(power));

            builder.DoForEach(a => a.PushPosition<Power>(p => p.Radix));
            power.Radix.Vist(this, builder);

            builder.DoForEach(a => a.PushPosition<Power>(p => p.Exponent));
            power.Exponent.Vist(this, builder);

            builder.DoForEach(a => a.PopPosition());
            return null;
        }
    }
}
